﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Domain.Core;
using System.Data.Entity;
using Domain.Core.Specification;
using Domain.Core.Extensions;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data.Entity.Infrastructure;
using System.Data;

namespace Infrastructure.Core
{
    public class DbContextRepository<TEntity> :
        ICompleteRepository<TEntity> where TEntity : class
    {
        protected DbContext _db { get; private set; }
        IUnitOfWork iUnitWork;
        public DbContextRepository(IUnitOfWork db, Action<string> _logger)
        {
            iUnitWork = db;
            _db = (DbContext)db;
            logger = _logger;
        }
        public DbContextRepository(IUnitOfWork db)
            : this(db, null)
        {

        }
        /// <summary>
        /// 日志委托
        /// </summary>
        Action<string> logger;

        #region IRepository<T> 成员

        public virtual void Add(TEntity item)
        {
            _db.Entry<TEntity>(item);
            _db.Set<TEntity>().Add(item);
            this.SaveChanges();
        }

        public virtual void Remove(TEntity item)
        {
            _db.Set<TEntity>().Attach(item);
            _db.Set<TEntity>().Remove(item);
            this.SaveChanges();
        }

        public virtual void Modify(TEntity item)
        {
            _db.Set<TEntity>().Attach(item);
            _db.Entry(item).State = EntityState.Modified;
            this.SaveChanges();
        }

        public void Modify(Expression<Action<TEntity>> entity)
        {

            TEntity newEntity = typeof(TEntity).GetConstructor(Type.EmptyTypes).Invoke(null) as TEntity;//建立指定类型的实例
            List<string> propertyNameList = new List<string>();
            MemberInitExpression param = entity.Body as MemberInitExpression;
            foreach (var item in param.Bindings)
            {
                string propertyName = item.Member.Name;
                object propertyValue;
                var memberAssignment = item as MemberAssignment;
                if (memberAssignment.Expression.NodeType == ExpressionType.Constant)
                {
                    propertyValue = (memberAssignment.Expression as ConstantExpression).Value;
                }
                else
                {
                    propertyValue = Expression.Lambda(memberAssignment.Expression, null).Compile().DynamicInvoke();
                }
                typeof(TEntity).GetProperty(propertyName).SetValue(newEntity, propertyValue, null);
                propertyNameList.Add(propertyName);
            }
            _db.Set<TEntity>().Attach(newEntity);
            _db.Configuration.ValidateOnSaveEnabled = false;
            var ObjectStateEntry = ((IObjectContextAdapter)_db).ObjectContext.ObjectStateManager.GetObjectStateEntry(newEntity);
            propertyNameList.ForEach(x => ObjectStateEntry.SetModifiedProperty(x.Trim()));
            this.SaveChanges();
            //  ((IObjectContextAdapter)_db).ObjectContext.Detach(newEntity);


        }
        public TEntity GetEntity(ISpecification<TEntity> specification)
        {
            return GetEntities().FirstOrDefault(specification.SatisfiedBy());
        }

        public IQueryable<TEntity> GetEntities()
        {
            return _db.Set<TEntity>().AsNoTracking();
        }

        public IQueryable<TEntity> GetEntities(ISpecification<TEntity> specification)
        {
            return GetEntities().Where(specification.SatisfiedBy());
        }

        public IEnumerable<TEntity> GetEntities(
            int pageIndex,
            int pageCount,
            Action<Orderable<TEntity>> order)
        {
            return GetEntities(null, pageIndex, pageCount, order);
        }

        public IEnumerable<TEntity> GetEntities(
            ISpecification<TEntity> specification,
            int pageIndex,
            int pageCount,
            Action<Orderable<TEntity>> order)
        {
            Orderable<TEntity> orderable;
            if (specification == null)
                orderable = new Orderable<TEntity>(GetEntities().AsQueryable());
            else
                orderable = new Orderable<TEntity>(GetEntities(specification).AsQueryable());

            order(orderable);
            return orderable.Queryable
                            .Skip(pageIndex * pageCount)
                            .Take(pageCount);
        }

        public IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate)
        {
            return GetEntities().Where(predicate);
        }

        #endregion

        #region IExtensionRepository<T> 成员

        public virtual void Add(IEnumerable<TEntity> item)
        {
            item.ToList().ForEach(i =>
            {
                this.Add(i);//不提交
            });
        }

        public virtual void Modify(IEnumerable<TEntity> item)
        {
            item.ToList().ForEach(i =>
            {
                this.Modify(i);
            });
        }

        public virtual void Remove(IEnumerable<TEntity> item)
        {
            item.ToList().ForEach(i =>
            {
                this.Remove(i);
            });
        }

        public TEntity Find(params object[] id)
        {
            return _db.Set<TEntity>().Find(id);
        }

        #endregion

        #region Protected Methods
        /// <summary>
        /// 根据工作单元的IsNotSubmit的属性，去判断是否提交到数据库
        /// 一般地，在多个repository类型进行组合时，这个IsNotSubmit都会设为true，即不马上提交，
        /// 而对于单个repository操作来说，它的值不需要设置，使用默认的false，将直接提交到数据库，这也保证了操作的原子性。
        /// </summary>
        protected virtual void SaveChanges()
        {
            try
            {
                if (!iUnitWork.IsNotSubmit)
                    iUnitWork.Save();
            }
            catch (Exception ex)
            {
                if (logger == null)
                    throw;
                logger(ex.Message);
            }

        }

        /// <summary>
        ///  计数更新,与SaveChange()是两个SQL链接，走分布式事务
        ///  子类可以根据自己的逻辑，去复写
        ///  tableName:表名
        ///  param:索引0为主键名，1表主键值，2为要计数的字段，3为增量
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="param">参数列表，索引0为主键名，1表主键值，2为要计数的字段，3为增量</param>
        protected virtual void UpdateForCount(string tableName, params object[] param)
        {
            string sql = "update [" + tableName + "] set [{2}]=ISNULL([{2}],0)+{3} where [{0}]={1}";
            List<object> listParasm = new List<object>
            {
                param[0],
                param[1],
                param[2],
                param[3],
            };
            _db.Database.ExecuteSqlCommand(string.Format(sql, listParasm.ToArray()));
        }
        #endregion

        #region IRepository<TEntity> 成员


        public TEntity GetEntity(Expression<Func<TEntity, bool>> predicate)
        {
            return this.GetEntities(predicate).FirstOrDefault();
        }

        #endregion

        public Domain.Core.ICompleteRepository<TEntity> ICompleteRepository
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }
    }
}
